#ifndef __SCHEDULE_H__
#define __SCHEDULE_H__

#include <memory>
#include "thread.h"
#include "fiber.h"


namespace johnsonli
{
	class Scheduler
	{
	public:
		typedef std::shared_ptr<Scheduler> ptr;
		typedef Mutex MutexType;

		/// <summary>
		/// 构造函数
		/// </summary>
		/// <param name="threads">线程数量</param>
		/// <param name="use_caller">是否将当前线程加入</param>
		/// <param name="name">协程调度器名称</param>
		Scheduler(size_t threads = 1, bool use_caller = true, const std::string& name = "");
		
		virtual ~Scheduler();

		const std::string& getName() { return m_name; }
		
		void start();
		void stop();

		//添加任务协程
		template<class FiberOrCb>
		void schedule(FiberOrCb fc, int thread = -1)
		{
			bool need_tickle = false;
			{
				MutexType::Lock lock(m_mutex);
				need_tickle = shceduleNoLock(fc, thread);
			}

			if (need_tickle)
			{
				tickle();
			}
			
		}

		//添加一组任务协程
		template<class InputIterator>
		void schedule(InputIterator begin, InputIterator end)
		{
			std::list<FiberAndCallBack> s;
			s.begin();
			bool need_tickle = false;
			{
				MutexType::Lock lock(m_mutex);
				while (begin != end)
				{
					need_tickle = shceduleNoLock(&*begin) || need_tickle;
					++begin;
				}
			}

			if (need_tickle)
			{
				tickle();
			}
		}

		/**
		 * @brief 是否有空闲线程
		 */
		bool hasIdleThreads() { return m_idleThreadCount > 0;}
		
		std::ostream& dump(std::ostream& os);

	private:
		template<class FiberOrCb>
		bool shceduleNoLock(FiberOrCb _fc, int thread=-1)
		{
			bool need_tickle = m_fibers.empty();
			FiberAndCallBack fc(_fc, thread);
			if (fc.fiber || fc.cb)
			{
				m_fibers.push_back(fc);
			}
			return need_tickle;
		}

	public:
		/// <summary>
		/// 获取当前协程调度器
		/// </summary>
		/// <returns>当前协程调度器</returns>
		static Scheduler* GetThis();

		/// <summary>
		/// 设置当前协程调度器
		/// </summary>
		static void SetThis(Scheduler* s);

		/// <summary>
		/// 获取当调度协程
		/// </summary>
		/// <returns>当前调度协程</returns>
		static Fiber* GetScheduleFiber();

	private:
		struct FiberAndCallBack
		{
			Fiber::ptr fiber;
			std::function<void()> cb;
			int thread;
			
			FiberAndCallBack(Fiber::ptr f, int thr)
				:fiber(f),
				thread(thr)
			{}

			FiberAndCallBack(Fiber::ptr* f, int thr)
				:thread(thr)
			{
				fiber.swap(*f);
			}

			FiberAndCallBack(std::function<void()> f , int thr)
				:cb(f),
				thread(thr)
			{}

			FiberAndCallBack(std::function<void()>* f, int thr)
				:thread(thr)
			{
				cb.swap(*f);
			}

			FiberAndCallBack()
				:thread(-1)
			{}

			void reset()
			{
				fiber = nullptr;
				cb = nullptr;
				thread = -1;
			}
		};

	protected:
		/// <summary>
		/// 协程调度函数
		/// </summary>
		void run();

		/// <summary>
		/// 协程无任务可调度时执行idle协程
		/// </summary>
		virtual void idle();

		/// <summary>
		/// 返回是否结束
		/// </summary>
		virtual bool stopping();

		/// <summary>
		/// 唤醒协程
		/// </summary>
		virtual void tickle();

		

	private:
		MutexType m_mutex;						//锁
		std::vector<Thread::ptr> m_threads;		//线程池
		std::list<FiberAndCallBack> m_fibers;	//即将执行和计划执行的协程，由协程完成具体任务
		Fiber::ptr m_schedulFiber;				//调度协程，只在use_caller = true有效
		std::string m_name;						//调度器名称
	
	protected:
		std::vector<int> m_threadIds;			//协程下的线程id数组
		int m_rootThread = 0;					//主线程id(use_caller = true才使用)
		size_t m_threadCount = 0;				//线程池中线程数量
		bool m_stopping = true;					//是否正在停止
		bool m_autoStop = false;				//是否自动停止
		 
		std::atomic<size_t> m_activeThreadCount = { 0 };//工作线程数量
		std::atomic<size_t> m_idleThreadCount = { 0 };//空闲线程数量
	};

	
}

#endif // !__SCHEDULE_H__

